/*
 * Copyright (C) 2013 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.settings;

import android.os.Bundle;
import android.preference.PreferenceFragment;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;

/*
 * Make PreferenceFragments focused after it is displayed.
 *
 * This class does the same thing what AutoFocusFragment class does
 * because we cannot make PreferenceFragment to extends AutoFocusFragment instead of Fragment.
 */
public class AutoFocusPreferenceFragment extends PreferenceFragment {

    private static final String TAG = "AutoFocusPreferenceFragment";

    private ViewTreeObserver.OnGlobalLayoutListener mGlobalLayoutListener;
    private boolean mRestored;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                /* requestFocus could fail at the first attempt in some cases.
                 * One example is calling addPreferencesFromResource in onResume.
                 * (see: LocationSettings, SecuritySettings) In that case, corresponding
                 * ListView's adapter is reset and ListView invalidates its view before
                 * inflate view.
                 * As a result, view traversal occurs twice -- one for empty view and
                 * another for drawing its content.
                 * To solve this issue, keeping listener until it success. */
                View v = getView();
                if (v != null && v.requestFocus()) {
                    v.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                }
            }
        };

        mRestored = false;
    }

    /* We have no choice but to give focus after the onResume
     * because some fragments finish update their UI in the onResume
     * to dynamically update themselves.
     */
    @Override
    public void onResume() {
        super.onResume();

        /* We only want to give focus if this fragment is resumed by user interaction within
         * the app, not by restored. (e.g. resumed by multitask)
         * See documentation on onSaveInstanceState to see how mRestored is managed.
         */
        if (!mRestored) {
            /* ViewTreeObserver.OnGlobalLayout would be the best solution here because
             *   - Listeners in ViewTreeObserver is attached to global view events
             *     while we only want to monitor this view.
             *   - OnLayoutChangeListener is called when view calculate its size
             *     after it finishes drawing its contents.
             */
            View v = getView();
            if (v != null) {
                v.getViewTreeObserver().addOnGlobalLayoutListener(mGlobalLayoutListener);
            }
        }
        mRestored = false;
    }

    @Override
    public void onPause() {
        // Just for sure.
        View v = getView();
        if (v != null) {
            v.getViewTreeObserver().removeOnGlobalLayoutListener(mGlobalLayoutListener);
        }

        super.onPause();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        /* According to Activity development page, this is called if Activities/Fragments
         * are vulnerable to destruction. (i.e. when the app goes to the background.)
         * As we don't want to give focus if the app is restored from the background (we'd better
         * keep previous focus in this case), set mRestored true here for future onResume.
         * Note that we cannot use onRestoreInstanceState and checking parameter to distinguish
         * whether it's restored because is is called only if this fragment is created again.
         */
        mRestored = true;
    }
}
